
#include "linux/stddef.h"

#include "i386/kernel/screen.h"
#include "common/linkage.h"
#include "linux/signal.h"
#include "linux/interrupt.h"
#include "linux/irq.h"

static u32 no_irq_startup(u32 irq) {
  puts("no_irq_startup");
  return 0;
}

static void no_irq_shutdown(u32 irq) {
  puts("no_irq_shutdown");
}

static void no_irq_enable(u32 irq) {
  puts("no_irq_enable");
}

static void no_irq_disable(u32 irq) {
  puts("no_irq_disable");
}

static void no_irq_ack(u32 irq) {
  puts("no_irq_ack");
}

static void no_irq_end(u32 irq) {
  puts("no_irq_end");
}

irqreturn_t no_action(int cpl, void* dev_id, struct pt_regs* reg) {
  return IRQ_NONE;
}

struct hw_interrupt_type no_irq_type = {
  .typename = "none",
  .startup = no_irq_startup,
  .end = no_irq_shutdown,
  .enable = no_irq_enable,
  .disable = no_irq_disable,
  .ack = no_irq_ack,
  .end = no_irq_end,
};

static int handle_IRQ_event(u32 irq, struct pt_regs *regs,
                            struct irqaction *action) {
  int ret, retval = 0, status = 0;
  if (!(action->flags & SA_INTERRUPT)) {
    // load_irq_enable();
  }

  do {
    ret = action->handler(irq, action->dev_id, regs);
    if (ret == IRQ_HANDLED)
      status |= action->flags;

    retval |= ret;
    action = action->next;
  } while (action);
  
  return retval;
}

asmlinkage u32 __do_IRQ(u32 irq, struct pt_regs* regs) {
  irq_desc_t *desc = irq_desc + irq;
  struct irqaction* action;
  u32 status;

  if (desc->status & IRQ_PER_CPU) {
    // TODO
    // CPU Local interrupts
    // no code here for tmp.
    return 1;
  }

  desc->handler->ack(irq);
  status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
  status |= IRQ_PENDING;

  action = NULL;
  if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
    action = desc->action;
    status &= ~IRQ_PENDING;
    status |= IRQ_INPROGRESS;
  }
  desc->status = status;

  if (!action) {
    goto out;
  }

  for (;;) {
    irqreturn_t action_ret;
    action_ret = handle_IRQ_event(irq, regs, action);
    if (!(desc->status & IRQ_PENDING)) {
      break;
    }
    desc->status &= ~IRQ_PENDING;
  }
  desc->status &= ~IRQ_INPROGRESS;
out:
  desc->handler->end(irq);
  return 1;
}
